package org.jeecg.modules.wx.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.UUIDGenerator;
import org.jeecg.common.util.oss.OssBootUtil;
import org.jeecg.modules.coupon.entity.Coupon;
import org.jeecg.modules.coupon.entity.CouponOrder;
import org.jeecg.modules.coupon.entity.MyCoupon;
import org.jeecg.modules.coupon.service.ICouponOrderService;
import org.jeecg.modules.coupon.service.ICouponService;
import org.jeecg.modules.coupon.service.IMyCouponService;
import org.jeecg.modules.device.controller.MenJinController;
import org.jeecg.modules.goodsorder.entity.GoodsOrder;
import org.jeecg.modules.goodsorder.service.IGoodsOrderService;
import org.jeecg.modules.order.entity.AddtimeOrder;
import org.jeecg.modules.order.entity.Order;
import org.jeecg.modules.order.service.IAddtimeOrderService;
import org.jeecg.modules.order.service.IOrderService;
import org.jeecg.modules.recharge.entity.Balance;
import org.jeecg.modules.recharge.entity.OrderRecharge;
import org.jeecg.modules.recharge.service.IBalanceService;
import org.jeecg.modules.recharge.service.IOrderRechargeService;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.jeecg.modules.system.util.DateUtil;
import org.jeecg.modules.system.util.MenJinUtil;
import org.jeecg.modules.system.util.RC4Util;
import org.jeecg.modules.zixishi.entity.Quyu;
import org.jeecg.modules.zixishi.entity.Seat;
import org.jeecg.modules.zixishi.entity.Zixishi;
import org.jeecg.modules.zixishi.service.IQuyuService;
import org.jeecg.modules.zixishi.service.ISeatService;
import org.jeecg.modules.zixishi.service.IZixishiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.awt.*;
import java.io.File;
import java.io.FileInputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Api(value = "小程序支付控制器",tags = "小程序支付")
@RequiredArgsConstructor
@RestController
@RequestMapping("/wxma/pay")
@Slf4j
public class PayController {

    @Autowired
    private WxPayService payService;

    @Autowired
    private IOrderService orderService;
    @Autowired
    private ICouponOrderService couponOrderService;
    @Autowired
    private ICouponService couponService;
    @Autowired
    private IMyCouponService myCouponService;
    @Autowired
    private IBalanceService balanceService;
    @Autowired
    private IOrderRechargeService rechargeService;
    @Autowired
    private IAddtimeOrderService addtimeOrderService;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private ISeatService seatService;
    @Autowired
    private IQuyuService quyuService;
    @Autowired
    private IZixishiService zixishiService;
    @Autowired
    private IGoodsOrderService goodsOrderService;
    @Autowired
    private RedisUtil redisUtil;


    @ApiOperation("微信统一下单，并组装所需支付参数")
    @PostMapping("/createorder")
    public Result<Object> payRecharge(@RequestBody Map orderMap) {
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
        Double amt = 100*Double.parseDouble("" + orderMap.get("totalAmt"));
        amount.setTotal(amt.intValue());
        request.setPayer(payer.setOpenid(sysUser.getThirdId()));
        request.setAmount(amount);
        request.setDescription("" + orderMap.get("description"));
        request.setOutTradeNo("" + orderMap.get("orderNum"));
        request.setAttach("" + orderMap.get("orderType"));
        try {
            Object orderV3 = this.payService.createOrderV3(TradeTypeEnum.JSAPI, request);
            return Result.ok(orderV3);
        } catch (WxPayException e) {
            e.printStackTrace();
            return Result.error("发生系统错误，请联系管理员");
        }
    }

    @ApiOperation(value = "支付回调通知处理")
    @RequestMapping("/notify/order")
    @Transactional(rollbackFor = Exception.class)
    public String parseOrderNotifyResult(@RequestBody String data) throws WxPayException {
        log.info(data);
        WxPayNotifyV3Result v3Result = this.payService.parseOrderNotifyV3Result((String) data, null);
        String attach = v3Result.getResult().getAttach();
        String outTradeNo = v3Result.getResult().getOutTradeNo();
        Float amt = v3Result.getResult().getAmount().getPayerTotal().floatValue()/100;
        log.info(attach + "," + outTradeNo);

        //加时订单回调
        if ("ADDTIMEORDER".equals(attach)){
            try {
                AddtimeOrder addtimeOrder = addtimeOrderService.getOne(new LambdaQueryWrapper<AddtimeOrder>().eq(AddtimeOrder::getOrderNum,outTradeNo));
                addtimeOrder.setPayStatus("1").setPayType("0").setPaytime(new Date());
                addtimeOrder.setMoney(String.format("%.2f",amt));
                addtimeOrderService.updateById(addtimeOrder);

                Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNum, addtimeOrder.getSeatorderNum()));
                order.setEndTime(DateUtil.addHours(order.getEndTime(),Integer.parseInt(addtimeOrder.getHours())));
//                order.setOrderMoney(new BigDecimal(order.getOrderMoney()).add(new BigDecimal(addtimeOrder.getMoney())).stripTrailingZeros().toPlainString());

                Seat seat = seatService.getById(order.getSeatId());
                Quyu quyu = quyuService.getById(order.getQuyuId());
                Zixishi zixishi = zixishiService.getById(order.getZixishiId());
                String url = MenJinUtil.genQrCode(order,seat,quyu,zixishi);
                order.setQrcode(url);
                orderService.updateById(order);
                int minutes = DateUtil.minuteDiff(new Date(), order.getEndTime());
                minutes = minutes>0?minutes:1;
                redisUtil.del("orderseat:xudan:" + order.getOrderNum());
                redisUtil.set("orderseat:end:" + order.getOrderNum(), "", 60*(minutes));
            }catch (Exception ex){
                ex.printStackTrace();
            }
        } else if("RECHARGE".equals(attach)){
            OrderRecharge recharge = rechargeService.getOne(new LambdaQueryWrapper<OrderRecharge>().eq(OrderRecharge::getOrderNum, outTradeNo));
            if(!StringUtils.equals(recharge.getPayStatus(),"1")){
                recharge.setPayStatus("1");
                recharge.setAmountTotal(String.format("%.2f",amt));
                rechargeService.updateById(recharge);
                Balance balance = balanceService.getOne(new LambdaQueryWrapper<Balance>().eq(Balance::getSysUserId,recharge.getSysUserId()));
                if(null!=balance){
                    balance.setBalance(balance.getBalance().add(new BigDecimal(recharge.getRechargeTotal())));
                }else{
                    balance = new Balance();
                    balance.setSysUserId(recharge.getSysUserId());
                    balance.setBalance(new BigDecimal(recharge.getRechargeTotal()));
                }
                balanceService.saveOrUpdate(balance);
            }
        } else if ("SEATORDER".equals(attach)) {
            try {
                String wxOrderId = v3Result.getResult().getTransactionId();
                Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNum, outTradeNo));
                redisUtil.del("orderseat:quxiao:" + order.getOrderNum());
                order.setOrderStatus("1").setPayTime(new Date()).setPayType("0");
                Seat seat = seatService.getById(order.getSeatId());
                Quyu quyu = quyuService.getById(order.getQuyuId());
                Zixishi zixishi = zixishiService.getById(order.getZixishiId());
                String url = MenJinUtil.genQrCode(order,seat,quyu,zixishi);

                order.setQrcode(url);
                order.setWxOrderId(wxOrderId);
                order.setOrderMoney(String.format("%.2f",amt));
                orderService.updateById(order);

                int minutes = DateUtil.minuteDiff(new Date(),order.getStartTime());
                minutes = minutes>0?minutes:1;
                redisUtil.set("orderseat:start:" + order.getOrderNum(), "",60*minutes);

            }catch (Exception ex){
                ex.printStackTrace();
            }
        } else if ("COUPON".equals(attach)) {
            try {
                String wxOrderId = v3Result.getResult().getTransactionId();
                CouponOrder order = couponOrderService.getOne(new LambdaQueryWrapper<CouponOrder>().eq(CouponOrder::getOrderNum, outTradeNo));
                order.setOrderStatus("1").setPayTime(new Date()).setPayType("0");
                order.setWxOrderId(wxOrderId);
                order.setOrderMoney(String.format("%.2f",amt));
                couponOrderService.updateById(order);
                Coupon coupon = couponService.getById(order.getCouponId());
                SysUser sysUser = sysUserService.getById(order.getSysUserId());

                MyCoupon myCoupon = myCouponService.getOne(new LambdaQueryWrapper<MyCoupon>().eq(MyCoupon::getOrderId,order.getId()));
                if(null == myCoupon) {
                    myCoupon = new MyCoupon();
                    myCoupon.setCouponName(coupon.getName())
                            .setDuration(coupon.getDuration())
                            .setDurationLeft(coupon.getDuration().doubleValue())
                            .setSysUserId(order.getSysUserId())
                            .setPhone(sysUser.getPhone())
                            .setCouponType(coupon.getType())
                            .setDisAmt(coupon.getDisAmt())
                            .setEffectEndDate(coupon.getEffectEndDate())
                            .setEffectStartDate(coupon.getEffectStartDate())
                            .setOrigAmt(coupon.getOrigAmt())
                            .setQuyuId(coupon.getQuyuId())
                            .setUseEndTime(coupon.getUseEndTime())
                            .setUseStartTime(coupon.getUseStartTime())
                            .setUseStatus(0)
                            .setMultipleFlag(coupon.getMultipleFlag())
                            .setZixishiId(coupon.getZixishiId())
                            .setOrderId(order.getId());
                    myCoupon.setEndDate(DateUtils.addDays(new Date(), coupon.getDays()));
                    myCouponService.save(myCoupon);
                }
                redisUtil.set("mycoupon:end:" + myCoupon.getId(), "",60*60*24*coupon.getDays());
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }else if("GOODSORDER".equals(attach)){
            String wxOrderId = v3Result.getResult().getTransactionId();
            GoodsOrder order = goodsOrderService.getOne(new LambdaQueryWrapper<GoodsOrder>().eq(GoodsOrder::getOrderNum, outTradeNo));
            order.setOrderStatus("1");
            order.setPayTime(new Date());
            order.setPayType("0");
            order.setWxOrderId(wxOrderId);
            order.setOrderMoney(String.format("%.2f",amt));
            goodsOrderService.updateById(order);
            goodsOrderService.sendMsg4Goods(order.getId());
            redisUtil.del("goodsorder:quxiao:" + order.getOrderNum());
        }

        return WxPayNotifyResponse.success("成功");
    }

    @ApiOperation(value = "支付回调通知处理")
    @RequestMapping("/notify/refund")
    public String refundNotifyResult(@RequestBody String data) throws WxPayException {
        WxPayRefundNotifyV3Result notifyResult = this.payService.parseRefundNotifyV3Result(data,null);
        System.out.println(notifyResult);
        if (null != notifyResult && notifyResult.getResult().getRefundStatus().equals("SUCCESS")) {
            String outTradeNo = notifyResult.getResult().getOutTradeNo();
            String outRefundNo = notifyResult.getResult().getOutRefundNo();
            if(StringUtils.startsWith(outRefundNo,"seatorder_return_")) {
                Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderNum, outTradeNo));
                order.setBackStatus("1");
                orderService.updateById(order);
            }else if(StringUtils.startsWith(outRefundNo,"couponorder_return_")) {
                CouponOrder couponOrder = couponOrderService.getOne(new LambdaQueryWrapper<CouponOrder>().eq(CouponOrder::getOrderNum, outTradeNo));
                if(null != couponOrder){
                    couponOrder.setBackStatus("1");
                    couponOrderService.updateById(couponOrder);
                    Coupon coupon = couponService.getById(couponOrder.getCouponId());
                    if(null != coupon) {
                        coupon.setCnt(coupon.getCnt()+1);
                        couponService.updateById(coupon);
                    }
                }
            }
        }
        return WxPayNotifyResponse.success("成功");
    }

}
